import os
import platform

import yapgvb

def generate_image(image_path, madefiles, group_name):
    graph = yapgvb.Digraph()

    file_nodes = {}

    # Find the toplevel directory show we can show relative file paths.
    toplevel_dir = None
    for madefile in madefiles:
        if toplevel_dir is None or len(madefile.dir) < len(toplevel_dir):
            toplevel_dir = madefile.dir

    # Build a graph between files and tasks.
    for madefile in madefiles:
        for task in madefile.tasks:
            if task.group_name != group_name:
                continue

            task_node = _create_task_node(task, graph)
            for input_file in (
                    task.input_files.contents +
                    task.files_that_must_exist.contents):
                edge = _get_file_node(
                    input_file, toplevel_dir, file_nodes, graph) >> task_node
                edge.color = (
                    "indigo" if task.input_files.contains(input_file)
                    else "yellow")

            for output_file in task.output_files.contents:
                edge = task_node >> _get_file_node(
                    output_file, toplevel_dir, file_nodes, graph)

    # Lay out the graph and generate an image or dot file from it.
    if image_path.endswith(".dot"):
        graph.write(image_path)
    else:
        graph.layout(yapgvb.engines.dot)
        graph.render(image_path)

def _create_task_node(task, graph):
    node = graph.add_node(
        task.description, shape="oval",
        label=_wrap_text(task.description))
    node.color = {
            task.states.completed: "green",
            task.states.running: "yellow",
            task.states.runnable: "blue",
            task.states.missing_input_files: "red",
            task.states.waiting_input_tasks: "indigo"
        }[task.state]
    return node

def _get_file_node(file_path, toplevel_dir, file_nodes, graph):
    if file_path in file_nodes:
        return file_nodes[file_path]

    relative_file_path = (file_path[len(toplevel_dir) + 1:]
        if file_path.startswith(toplevel_dir) else file_path)
    node = graph.add_node(
        relative_file_path, shape="box", label=_wrap_text(relative_file_path))
    if not os.path.exists(file_path):
        node.color = "red"
    file_nodes[file_path] = node
    return node

def _wrap_text(text):
    chunk_size = 20
    return "\\n".join(text[start:start + chunk_size]
        for start in range(0, len(text), chunk_size))

# TODO: Find a portable way to do this.
if platform.system() == "Windows":
    os.environ["PATH"] += ";c:\\Program Files (x86)\\Graphviz 2.28\\bin"

